package com.baijia.web.controller.v1;

import com.baijia.authentication.AccountType;
import com.baijia.authentication.principal.Credentials;
import com.baijia.common.enums.ResponseStatus;
import com.baijia.dao.ThirdLoginAccountDao;
import com.baijia.dao.po.ThirdLoginAccount;
import com.baijia.exception.ErrorCode;
import com.baijia.services.IAccountService;
import com.baijia.services.PassportService;
import com.baijia.services.UserService;
import com.baijia.util.JigouPasswordUtil;
import com.baijia.util.MobileLoginUtil;
import com.baijia.util.RedisUtil;
import com.baijia.web.constant.Const;
import com.baijia.web.controller.AbstractAuthorizeController;
import com.baijia.web.dto.LoginInfoDto;
import com.baijia.web.dto.request.ExistedUserMobileRequest;
import com.baijia.web.dto.response.MobileResponse;
import com.baijia.web.service.IPassportValidateService;
import com.baijia.web.util.ChannelEnum;
import com.baijia.web.util.LoginAndLogoutUtil;
import com.baijia.web.util.MD5Util;
import com.baijia.web.vo.BindMobileVO;
import com.baijia.web.vo.PasswordRequestVO;
import com.baijia.web.vo.ResetPasswordVO;

import com.alibaba.fastjson.JSON;
import com.beust.jcommander.internal.Maps;
import com.google.code.kaptcha.Producer;
import com.google.common.base.Preconditions;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller
@RequestMapping(value = "/v1/rest")
public class RestAccountController extends AbstractAuthorizeController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private Producer captchaProducer;

    @Resource
    private PassportService passportService;

    @Resource
    private UserService userService;

    @Resource
    private IAccountService accountService;

    @Resource
    private IPassportValidateService passportValidateService;

    @Resource
    private ThirdLoginAccountDao thirdLoginAccountDao;

    @RequestMapping(value = "/password.json")
    @ResponseBody
    public MobileResponse updatePwd(HttpServletRequest request, HttpServletResponse httpServletResponse,
        PasswordRequestVO passwordRequestVO) {
        MobileResponse response = new MobileResponse();

        LoginInfoDto loginInfoDto = getLoginInfo(request, passwordRequestVO.getAppId());

        if (loginInfoDto != null && loginInfoDto.getAccountId() != null) {
            passwordRequestVO.setPassportId(loginInfoDto.getAccountId());
        } else {
            response.fail().msg(ErrorCode.LOGIN_NEED_ERROR.getMessage());
            return response;
        }
        if (!passportValidateService.validatePassword(passwordRequestVO, response)) {
            return response;
        }
        String password = JigouPasswordUtil.createHash(passwordRequestVO.getNewPwd());
        if (passportService.updatePwd(passwordRequestVO.getPassportId(), password) > 0) {
            /** 修改完密码需要重新登录 */
            String ticketGrantingTicketId = cookieRetrievingCookieGenerator.retrieveCookieValue(request);
            handleLogout(ticketGrantingTicketId, request, httpServletResponse);
            response.msg(ErrorCode.OK.getMessage());
        } else {
            response.fail().msg(ErrorCode.UPDATE_PASSWORD_FAIL.getMessage());
        }

        return response;
    }

    @RequestMapping(value = "/resetPassword.json")
    @ResponseBody
    public MobileResponse forgetPwd(ResetPasswordVO resetPasswordVO, HttpServletRequest request,
        HttpServletResponse httpServletResponse) {
        MobileResponse response = new MobileResponse();
        if (!passportValidateService.validateResetPassword(resetPasswordVO, response)) {
            return response;
        }
        if (passportService.updatePwdByMobile(resetPasswordVO.getPassword(), resetPasswordVO.getMobile()) > 0) {
            String ticketGrantingTicketId = cookieRetrievingCookieGenerator.retrieveCookieValue(request);
            handleLogout(ticketGrantingTicketId, request, httpServletResponse);
            response.msg(ErrorCode.OK.getMessage());
        } else {
            response.fail().msg(ErrorCode.RE_PASSWORD_ERROR.getMessage());
        }
        return response;
    }

    @RequestMapping(value = "/validateMobile.json")
    @ResponseBody
    public MobileResponse validateMobile(String mobile, String code, Integer appId, HttpServletRequest request) {
        MobileResponse response = new MobileResponse();
        if (appId == null) {
            response.fail().msg(ResponseStatus.PARAM_NULL_APPID.getMsg());
            return response;
        }
        LoginInfoDto loginInfoDto = getLoginInfo(request, appId);

        if (loginInfoDto != null) {
            if (mobile.equals(loginInfoDto.getMobile())) {
                if (MobileLoginUtil.validPasscode(mobile, code)) {
                    response.msg(ErrorCode.OK.getMessage());
                } else {
                    response.fail().msg(ErrorCode.CODE_INVALID_ERROR.getMessage());
                }
            } else {
                response.fail().msg(ErrorCode.VALIDATE_MOBILE_ERROR.getMessage());
            }
        } else {
            response.fail().msg(ErrorCode.LOGIN_NEED_ERROR.getMessage());
        }

        return response;
    }

    @RequestMapping(value = "/existedUserMobile.json")
    @ResponseBody
    public MobileResponse<Map<String, Object>> existedUserMobile(ExistedUserMobileRequest existedUserMobileRequest,
        HttpServletRequest request, HttpServletResponse httpServletResponse) {

        MobileResponse<Map<String, Object>> response = new MobileResponse<>();

        try {
            Integer appId = existedUserMobileRequest.getAppId();
            if (appId == null) { // 根据userRole和orgType推算appId
                AccountType accountType =
                    LoginAndLogoutUtil.getByUserRoleType(request.getParameter("userRole"),
                        request.getParameter("orgType"));
                appId = accountType.getCode();
            }
            logger.info("existedUserMobile : appId={}", appId);

            Map<String, Object> resultData = Maps.newHashMap();
            response.setData(resultData);

            resultData.put(
                "isRegisted",
                passportService.existedUserI18n(existedUserMobileRequest.getCountrySymbol(),
                    existedUserMobileRequest.getCountryCode(), existedUserMobileRequest.getMobile(), appId));

            response.msg(ErrorCode.OK.getMessage());
        } catch (Exception e) {
            response.fail().msg(ErrorCode.SYSTEM_ERROR.getMessage());
        }

        return response;
    }

    @RequestMapping(value = "/bindMobile.json")
    @ResponseBody
    public MobileResponse<?> bindMobile(BindMobileVO bindMobileVO, HttpServletRequest request,
        HttpServletResponse httpServletResponse) {

        MobileResponse<?> response = new MobileResponse<>();

        try {
            Integer appId = bindMobileVO.getAppId();
            if (appId == null) { // 根据userRole和orgType推算appId
                AccountType accountType =
                    LoginAndLogoutUtil.getByUserRoleType(request.getParameter("userRole"),
                        request.getParameter("orgType"));
                appId = accountType.getCode();
            }
            logger.info("bindMobile : appId={}", appId);

            // 验证用户是否已登录
            LoginInfoDto loginInfoDto = getLoginInfo(request, appId);
            if (loginInfoDto == null) {
                response.fail().msg(ErrorCode.LOGIN_NEED_ERROR.getMessage());
                return response;
            }

            // 手机号国际化
            String mobile = bindMobileVO.getNewMobile();
            String countrySymbol = bindMobileVO.getCountrySymbol();
            String countryCode = bindMobileVO.getCountryCode();

            // 验证验证码是否正确
            if (!MobileLoginUtil.validatePasscodeI18n(countrySymbol, countryCode, mobile, bindMobileVO.getCode())) {
                response.fail().msg(ErrorCode.CODE_INVALID_ERROR.getMessage());
                return response;
            }

            // 验证手机号是否已存在
            if (passportService.existedUserI18n(countrySymbol, countryCode, mobile, appId)) {
                response.fail().msg(ErrorCode.MOBILE_ALREADY_BOUND.getMessage());
                return response;
            }

            if (!accountService.updateMobileByAccountId(appId, loginInfoDto.getAccountId(), countrySymbol, countryCode,
                mobile)) { // 更新失败
                response.fail().msg(ErrorCode.BIND_MOBILE_ERROR.getMessage());
                return response;
            }

            String ticketGrantingTicketId = getTgtId(request, appId);
            handleLogout(ticketGrantingTicketId, request, httpServletResponse); // 退出当前登录

            response.msg(ErrorCode.OK.getMessage());
            return response;
        } catch (Exception e) {
            logger.info("bindMobile execution error", e);
            response.fail().msg(ErrorCode.SYSTEM_ERROR.getMessage());
            return response;
        }
    }

    @RequestMapping(value = "/mobileUpdate/authentication.json")
    @ResponseBody
    public MobileResponse<Map<String, String>> authentication(HttpServletRequest request) {
        MobileResponse<Map<String, String>> response = new MobileResponse<>();
        Map<String, String> resultMap = new HashMap<>();
        response.setData(resultMap);

        try {
            if (passportService.changeMobileVerify(request)) {
                resultMap.put("result", "true");
            } else {
                resultMap.put("result", "false");

                String type = request.getParameter("type");
                if ("1".equals(type)) {
                    response.fail().msg(ErrorCode.PAYMENT_PASSWORD_VALIDATION_ERROR.getMessage());
                } else if ("0".equals(type)) {
                    response.fail().msg(ErrorCode.BANK_CARD_VALIDATION_ERROR.getMessage());
                } else {
                    response.fail().setMsg("验证失败.");
                }
            }
        } catch (Exception e) {
            logger.warn("authentication failed", e);
            response.fail().msg(ErrorCode.SYSTEM_ERROR.getMessage());
        }

        logger.debug("Respnose : {}", response);
        return response;
    }

    @RequestMapping(value = "/mobileUpdate/bankCard.json")
    @ResponseBody
    public MobileResponse<List<Map<String, Object>>> bankCard(HttpServletRequest request) {

        MobileResponse<List<Map<String, Object>>> response = new MobileResponse<>();

        try {
            List<Map<String, Object>> result = passportService.getBankCardInfo(request);
            if (result == null) {
                response.fail().setMsg("获取银行卡列表出现异常.");
            } else {
                response.setData(result);
            }
        } catch (Exception e) {
            logger.warn("getBankCardInfo failed", e);
            response.fail().msg(ErrorCode.SYSTEM_ERROR.getMessage());
        }

        logger.debug("Respnose : {}", response);
        return response;
    }

    @RequestMapping(value = "/userInfoThird.json")
    @ResponseBody
    public MobileResponse userInfoThird(HttpServletRequest request, String third_id) {
        MobileResponse response = new MobileResponse();
        ThirdLoginAccount thirdLoginAccount = thirdLoginAccountDao.getById(Long.valueOf(third_id));
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("channel", ChannelEnum.getChannelByKey(thirdLoginAccount.getChannel()).getValue());
        resultMap.put("nickname", thirdLoginAccount.getNickName());
        response.data(resultMap);
        return response;
    }

    @RequestMapping("/country.json")
    @ResponseBody
    public MobileResponse getCountryCode() {
        MobileResponse response = new MobileResponse();
        File file;
        try {
            file = Paths.get(this.getClass().getClassLoader().getResource("flag.json").toURI()).toFile();
        } catch (URISyntaxException e) {
            response.fail().msg(ErrorCode.SYSTEM_ERROR.getMessage());
            return response;
        }
        Scanner scanner = null;
        StringBuilder buffer = new StringBuilder();
        try {
            scanner = new Scanner(file, "utf-8");
            while (scanner.hasNextLine()) {
                buffer.append(scanner.nextLine());
            }
        } catch (FileNotFoundException e) {
            response.fail().msg(ErrorCode.SYSTEM_ERROR.getMessage());
            return response;
        } finally {
            if (scanner != null) {
                scanner.close();
            }
        }
        com.alibaba.fastjson.JSONArray result = JSON.parseArray(buffer.toString());
        response.setData(result);
        response.setCode(ErrorCode.OK.getCode());
        return response;
    }

    @RequestMapping("/captcha.json")
    public ModelAndView getCaptchaImage(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String guid = request.getParameter(Credentials.CAPTCHA_GUID);

        response.setDateHeader("Expires", 0);
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        response.setHeader("Pragma", "no-cache");
        response.setContentType("image/jpeg");
        String capText = captchaProducer.createText();
        RedisUtil.hSet(guid, Credentials.CAPTCHA_GUID, capText, Const.CAPTCHA_EXPIRE);

        BufferedImage bi = captchaProducer.createImage(capText);
        ServletOutputStream out = response.getOutputStream();
        ImageIO.write(bi, "jpg", out);
        try {
            out.flush();
        } finally {
            out.close();
        }
        return null;
    }

    @RequestMapping("/captchaTest.json")
    public MobileResponse captchaTest(HttpServletRequest request) throws Exception {
        MobileResponse<Map<String, String>> response = new MobileResponse<>();
        String guid = request.getParameter(Credentials.CAPTCHA_GUID);
        String capText = captchaProducer.createText();
        RedisUtil.hSet(guid, Credentials.CAPTCHA_GUID, capText, Const.CAPTCHA_EXPIRE);
        Map<String, String> map = new HashMap<>();
        map.put("captcha", capText);
        response.setData(map);

        return response;
    }

    @RequestMapping(value = "/qrCode.json")
    public void getqcode(HttpServletRequest request, HttpServletResponse resp, String uuid,
        @RequestParam(required = false) String size) throws IOException {
        int imgSize = Const.IMG_SIZE;
        String url = request.getScheme() + "://" + request.getServerName() + Const.QRCODE_REDIRECT_SUFFIX + uuid;
        if (StringUtils.isNotBlank(size)) {
            imgSize = Integer.valueOf(size);
        }
        RedisUtil.hSet(uuid, Const.UUID, uuid, Const.QRCODE_LIVE_TIME);
        if (url != null && !"".equals(url)) {
            ServletOutputStream stream = null;
            try {
                int width = imgSize;// 图片的宽度
                int height = imgSize;// 高度
                stream = resp.getOutputStream();
                QRCodeWriter writer = new QRCodeWriter();
                HashMap hints = new HashMap();
                hints.put(EncodeHintType.MARGIN, 0);
                BitMatrix m = writer.encode(url, BarcodeFormat.QR_CODE, height, width, hints);
                MatrixToImageWriter.writeToStream(m, "png", stream);
            } catch (WriterException e) {
                e.printStackTrace();
            } finally {
                if (stream != null) {
                    stream.flush();
                    stream.close();
                }
            }
        }
    }

    @RequestMapping(value = "/qrCodeLogin.json")
    @ResponseBody
    public MobileResponse qrCodeLogin(String appId, String username, String countryCode, String token) {

        MobileResponse response = new MobileResponse();
        try {
            Preconditions.checkArgument(StringUtils.isNotBlank(appId), "appId can not be null!");
            Preconditions.checkArgument(StringUtils.isNotBlank(username), "username can not be null!");
            Preconditions.checkArgument(StringUtils.isNotBlank(countryCode), "countryCode can not be null!");
            Preconditions.checkArgument(StringUtils.isNotBlank(token), "token can not be null!");
        } catch (IllegalArgumentException e) {
            response.fail().msg(e.getMessage());
            return response;
        }
        String tokenStr = MD5Util.md5(appId + username + countryCode);
        if (tokenStr.equalsIgnoreCase(token)) {
            if (RedisUtil.hGet(username, Const.UUID) != null) {
                String uuid = RedisUtil.hGet(username, Const.UUID);
                RedisUtil.hSet(uuid, Const.APPID, appId, Const.QRCODE_LIVE_TIME);
                RedisUtil.hSet(uuid, Const.USERNAME, username, Const.QRCODE_LIVE_TIME);
                response.msg("success");
            } else { // 二维码已经过期
                response.fail().msg(ErrorCode.QRCODE_EXPIRED_ERROR.getMessage());
            }
        } else {
            response.fail().msg("the token is error!");
        }

        return response;
    }

    @RequestMapping(value = "/scanCancel.json")
    @ResponseBody
    public MobileResponse scanCancel(String username) {

        MobileResponse response = new MobileResponse();
        try {
            Preconditions.checkArgument(StringUtils.isNotBlank(username), "username can not be null!");
        } catch (IllegalArgumentException e) {
            response.fail().msg(e.getMessage());
            return response;
        }

        if (RedisUtil.hGet(username, Const.UUID) != null) {
            String uuid = RedisUtil.hGet(username, Const.UUID);
            RedisUtil.hDel(uuid, Const.UUID);
            response.msg("success");
        } else { // 二维码已经过期
            response.fail().msg(ErrorCode.QRCODE_EXPIRED_ERROR.getMessage());
        }

        return response;
    }

    @RequestMapping(value = "/scanSuccess.json")
    @ResponseBody
    public MobileResponse scanSuccess(String uuid, String username) {

        MobileResponse response = new MobileResponse();
        try {
            Preconditions.checkArgument(StringUtils.isNotBlank(uuid), "uuid can not be null!");
            Preconditions.checkArgument(StringUtils.isNotBlank(username), "username can not be null!");
        } catch (IllegalArgumentException e) {
            response.fail().msg(e.getMessage());
            return response;
        }
        if (RedisUtil.hGet(uuid, Const.UUID) != null) {
            RedisUtil.hSet(username, Const.UUID, uuid, Const.QRCODE_LIVE_TIME);
            RedisUtil.hSet(uuid, Const.SCAN_QRCODE_SUCCESS, "true", Const.QRCODE_LIVE_TIME);
            response.msg("success");
        } else { // 二维码已经过期
            response.fail().msg(ErrorCode.QRCODE_EXPIRED_ERROR.getMessage());
        }
        return response;
    }

}
